package web

import (
	"JK-Junior-Go-Engineer-Camp/webook/internal/service"
	"JK-Junior-Go-Engineer-Camp/webook/internal/service/oauth2/wechat"
	ijwt "JK-Junior-Go-Engineer-Camp/webook/internal/web/jwt"
	"JK-Junior-Go-Engineer-Camp/webook/pkg/ginx"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	uuid "github.com/lithammer/shortuuid/v4"
	"net/http"
)

// OAuth2WechatHandler 处理与微信有关的请求
type OAuth2WechatHandler struct {
	svc     wechat.Service
	userSvc service.UserService
	ijwt.Handler
	key             []byte // JWT 的 key 这里的 jwt是为了了存储 state，避免微信登录出现 CSRF 问题
	stateCookieName string // 存储了 state 的 cookie 的 name
}

func NewOAuth2WechatHandler(svc wechat.Service, userSvc service.UserService,
	hdl ijwt.Handler) *OAuth2WechatHandler {
	return &OAuth2WechatHandler{
		svc:             svc,
		userSvc:         userSvc,
		key:             []byte("Xw4Gxn3nwFTyG9PM1YUzhfEB27bEnntB"),
		stateCookieName: "jwt-state",
		Handler:         hdl,
	}
}

// RegisterRoutes 注册微信相关路由
func (o *OAuth2WechatHandler) RegisterRoutes(server *gin.Engine) {
	wechatGroup := server.Group("/oauth2/wechat")
	wechatGroup.GET("/authurl", o.OAuth2URL)
	// 这里用 Any() 以防万一
	wechatGroup.Any("/callback", o.Callback)
}

// OAuth2URL 构造微信跳转URL返回给前端
func (o *OAuth2WechatHandler) OAuth2URL(ctx *gin.Context) {
	state := uuid.New()
	authURL, err := o.svc.AuthURL(ctx, state)
	if err != nil {
		ctx.JSON(http.StatusOK, ginx.Result{
			Code: 5, // 代表服务器错误
			Msg:  "构造微信跳转URL失败",
		})
		return
	}
	err = o.setStateCookie(ctx, state)
	if err != nil {
		ctx.JSON(http.StatusOK, ginx.Result{
			Msg:  "服务器异常",
			Code: 5,
		})
	}
	ctx.JSON(http.StatusOK, ginx.Result{Data: authURL})
}

// Callback 微信扫码登录回调接口
func (o *OAuth2WechatHandler) Callback(ctx *gin.Context) {
	err := o.verifyState(ctx)
	if err != nil {
		ctx.JSON(http.StatusOK, ginx.Result{
			Code: 4, // 代表客户端错误
			Msg:  "非法请求",
		})
		return
	}
	// 这俩东西，你校验与否，意义都不大，如果code为空，你调用 svc的时候就会返回 err的
	code := ctx.Query("code")
	//state := ctx.Query("state")
	wechatInfo, err := o.svc.VerifyCode(ctx, code)
	if err != nil {
		ctx.JSON(http.StatusOK, ginx.Result{
			Code: 4, // 代表客户端错误
			Msg:  "授权码有误",
		})
		return
	}
	// 注册 or 登录
	domainUser, err := o.userSvc.FindOrCreateByWechat(ctx, wechatInfo)
	if err != nil {
		ctx.JSON(http.StatusOK, ginx.Result{
			Code: 5, // 代表服务端错误
			Msg:  "系统错误",
		})
		return
	}
	err = o.SetLoginToken(ctx, domainUser.Id)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
	}
	ctx.JSON(http.StatusOK, ginx.Result{
		Msg: "OK",
	})
}

// StateClaims 利用 JWT 存储 state
type StateClaims struct {
	jwt.RegisteredClaims
	State string
}

// 生成 存储了state 的 jwt，然后放到 cookie 里面
func (o *OAuth2WechatHandler) setStateCookie(ctx *gin.Context, state string) error {
	claims := StateClaims{State: state}
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
	tokenStr, err := token.SignedString(o.key)
	if err != nil {
		ctx.JSON(http.StatusOK, ginx.Result{
			Msg:  "服务器异常",
			Code: 5,
		})
		return err
	}
	ctx.SetCookie(o.stateCookieName, tokenStr, 600,
		// 限制一下 cookie 生效的 path
		"/oauth2/wechat/callback",
		// domain: 线上环境就配置成你线上环境的域名即可
		// secure: 是否使用https，线上环境设置为 true
		"", false,
		true,
	)
	return nil
}

// 校验 微信回调回来的 state 与 我们生成 state 是否一致
func (o *OAuth2WechatHandler) verifyState(ctx *gin.Context) error {
	state := ctx.Query("state")
	stateToken, err := ctx.Cookie(o.stateCookieName)
	if err != nil {
		return fmt.Errorf("无法获得cookie，%w", err)
	}
	var stateClaims StateClaims
	_, err = jwt.ParseWithClaims(stateToken, &stateClaims, func(token *jwt.Token) (interface{}, error) {
		return o.key, nil
	})
	if err != nil {
		return fmt.Errorf("解析token失败，%w", err)
	}
	if stateClaims.State != state { // state 不匹配
		return errors.New("state 不匹配")
	}
	return nil
}
